package binance

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"websocket-binance/internal/config"
)

// BinanceClient 管理与币安WebSocket的连接

type BinanceClient struct {
	config         *config.BinanceConfig
	connections    map[int]*websocket.Conn
	connMutex      sync.Mutex
	nextConnID     int
	subscriptions  map[string]map[string]bool // symbol -> subscription types
	messageHandler func(data []byte)
	ctx            context.Context
	cancel         context.CancelFunc
	wg             sync.WaitGroup
}

// SubscriptionMessage 定义订阅消息格式
type SubscriptionMessage struct {
	Method string                 `json:"method"`
	Params []string               `json:"params,omitempty"`
	ID     int                    `json:"id,omitempty"`
}

// NewBinanceClient 创建一个新的币安WebSocket客户端
func NewBinanceClient(cfg *config.BinanceConfig, messageHandler func(data []byte)) *BinanceClient {
	ctx, cancel := context.WithCancel(context.Background())
	client := &BinanceClient{
		config:         cfg,
		connections:    make(map[int]*websocket.Conn),
		nextConnID:     0,
		subscriptions:  make(map[string]map[string]bool),
		messageHandler: messageHandler,
		ctx:            ctx,
		cancel:         cancel,
	}

	// 初始化订阅映射
	for _, symbol := range cfg.Symbols {
		client.subscriptions[symbol] = make(map[string]bool)
	}

	return client
}

// Start 启动客户端并建立连接
func (c *BinanceClient) Start() error {
	// 建立最大连接数
	connectionsToCreate := min(c.config.MaxConnections, len(c.subscriptions))
	for i := 0; i < connectionsToCreate; i++ {
		if err := c.createConnection(); err != nil {
			return err
		}
	}

	// 分发订阅到不同连接
	c.distributeSubscriptions()

	return nil
}

// createConnection 创建一个新的WebSocket连接
func (c *BinanceClient) createConnection() error {
	conn, _, err := websocket.DefaultDialer.Dial(c.config.WSURL, nil)
	if err != nil {
		return fmt.Errorf("无法连接到币安WebSocket: %v", err)
	}

	connID := c.nextConnID
	c.nextConnID++

	c.connMutex.Lock()
	c.connections[connID] = conn
	c.connMutex.Unlock()

	// 启动接收循环
	c.wg.Add(1)
	go c.receiveLoop(connID, conn)

	return nil
}

// receiveLoop 处理从连接接收的消息
func (c *BinanceClient) receiveLoop(connID int, conn *websocket.Conn) {
	defer func() {
		c.wg.Done()
		conn.Close()
		
		c.connMutex.Lock()
		delete(c.connections, connID)
		c.connMutex.Unlock()
		
		// 重新连接
		sleepDuration, _ := time.ParseDuration(c.config.RetryInterval)
		timer := time.NewTimer(sleepDuration)
		select {
		case <-timer.C:
			if err := c.createConnection(); err != nil {
				log.Printf("重新连接失败: %v", err)
			}
		case <-c.ctx.Done():
			timer.Stop()
		}
	}()

	// 设置读取限制为10MB
	conn.SetReadLimit(10 * 1024 * 1024)

	for {
		select {
		case <-c.ctx.Done():
			return
		default:
			_, message, err := conn.ReadMessage()
			if err != nil {
				log.Printf("读取消息错误: %v", err)
				return
			}

			// 处理接收到的消息
			if c.messageHandler != nil {
				c.messageHandler(message)
			}
		}
	}
}

// distributeSubscriptions 分发订阅到不同的连接
func (c *BinanceClient) distributeSubscriptions() {
	c.connMutex.Lock()
	defer c.connMutex.Unlock()

	if len(c.connections) == 0 {
		return
	}

	// 为每个连接分配一些订阅
	symbolIndex := 0
	connIDs := make([]int, 0, len(c.connections))
	for id := range c.connections {
		connIDs = append(connIDs, id)
	}

	// 为每个符号设置订阅
	for _, symbol := range c.config.Symbols {
		connID := connIDs[symbolIndex%len(connIDs)]
		symbolIndex++

		conn := c.connections[connID]
		if conn == nil {
			continue
		}

		// 构建订阅参数
		params := []string{}
		// 币安WebSocket要求交易对使用小写
		lowercaseSymbol := strings.ToLower(symbol)
		for _, subType := range c.config.SubscriptionTypes {
			if subType == "kline" {
				for _, interval := range c.config.KlineIntervals {
					params = append(params, fmt.Sprintf("%s@kline_%s", lowercaseSymbol, interval))
				}
			} else {
				params = append(params, fmt.Sprintf("%s@%s", lowercaseSymbol, subType))
			}
		}

		// 发送订阅消息
		subMsg := SubscriptionMessage{
			Method: "SUBSCRIBE",
			Params: params,
			ID:     symbolIndex,
		}

		msgBytes, err := json.Marshal(subMsg)
		if err != nil {
			log.Printf("序列化订阅消息失败: %v", err)
			continue
		}

		if err := conn.WriteMessage(websocket.TextMessage, msgBytes); err != nil {
			log.Printf("发送订阅消息失败: %v", err)
		}

		// 更新订阅状态
		for _, subType := range c.config.SubscriptionTypes {
			c.subscriptions[symbol][subType] = true
		}
	}
}

// Stop 停止客户端并关闭所有连接
func (c *BinanceClient) Stop() {
	c.cancel()
	c.wg.Wait()
}

// 辅助函数：获取两个数中的最小值
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}